import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { plainToClass } from 'class-transformer';
import { SysConstants } from 'src/common/const/constants';
import { IOption } from 'src/common/dto/option.dto';
import { isNumberString } from 'src/common/utils';
import { DataSource, Repository } from 'typeorm';
import { Track } from '../track/entities/track.entity';
import { TrackCate } from '../track_cate/enitity/track_cate.entity';
import { DCategory } from './dao/cate.dao';
import { CategoryForm } from './dto/create-category.dto';
import { CategoryPageQuery } from './dto/search.dto';
import { Category } from './entities/category.entity';
import { CategoryVO } from './vo/category.vo';

@Injectable()
export class CategoryService {

  constructor(
    @InjectRepository(Category) private cateRepository: Repository<Category>,
    private dataSource: DataSource,
    private dcate: DCategory,
  ) { }

  /**
   * 类别-根据父类别id获取列表
   */
  getCategoryListById(id: number) {
    return this.dcate.getCategoryListById(id)
  }
  /**
   * 类别-更新
   * @param categoryId 
   * @param categoryForm 
   * @returns 
   */
  updateCategory(categoryId: number, categoryForm: CategoryForm) {
    return this.cateRepository.update(categoryId, categoryForm);
  }
  /**
   * 类别-单条数据
   * @param id 
   * @returns 
   */
  getCategoryFormData(id: number) {
    return this.findOne(id);
  }

  /**
   *  类别-下拉选项
   * @returns 
   */
  async getCateOptions(): Promise<IOption<string, number>[]> {

    const data: Category[] = await this.cateRepository.find({ where: { parentId: 0 } })
    return data.map<IOption<string, number>>(cate => ({ label: cate.categoryName, value: cate.id }))
  }
  /**
   * 类别-id-下拉选项
   * @returns 
   */
  async getCateAllOptions(id: any): Promise<IOption<string, number>[]> {


    const data: Category[] = await this.cateRepository.find()
    return this.generateGroupOptions(SysConstants.ROOT_CATE_ID, data).filter((option) => {
      if (isNumberString(id) || typeof id === 'number') {
        return option.value === +id
      }
      return true
    });
  }


  /**
   * 类别-新增
   * @param categoryForm 
   * @returns 
   */
  saveCategory(categoryForm: CategoryForm) {
    return this.cateRepository.save(plainToClass(Category, categoryForm))
  }


  /**
   * 类别-分页查询
   * @param param0 
   * @returns 
   */
  async findAll({ keywords }: CategoryPageQuery) {
    const build = await this.cateRepository
      .createQueryBuilder('cate')
      .leftJoin(TrackCate, "track_cate", "cate.id = track_cate.cate_id")
      .leftJoinAndMapMany('cate.tracks', Track, 'track', 'track.id = track_cate.track_id')
      .select([
        'cate.id',
        'cate.categoryName',
        'cate.parentId',
        'cate.icon',
        'cate.sort',
        'cate.isActive',
        'cate.createTime',
        'track.id',
      ]).orderBy("cate.id", 'ASC')


    if (keywords) {
      build.where('cate.categoryName like :keywords', { keywords: `%${keywords}%` });
    }

    const data = await build.getMany();

    return this.generateCates(SysConstants.ROOT_CATE_ID, data);

  }

  private generateCates(parentId: number, cates: Category[]) {

    const temArr = []

    cates.forEach(cate => {
      if (cate.parentId == parentId) {
        const a = plainToClass(CategoryVO, cate)
        const subCate = this.generateCates(cate.id, cates)
        if (subCate.length > 0) {
          a.subCate = [...subCate]
        }
        if (cate.parentId != 0) {
          a.count = cate?.tracks?.length ?? 0;
        }

        temArr.push(a)
      }

    })
    return temArr
  }


  private generateGroupOptions(parentId: number, cates: Category[]): IOption<string, number>[] {

    const temArr = []

    cates.forEach(cate => {
      if (cate.parentId == parentId) {
        const option: IOption<string, number> = new IOption()
        option.label = cate.categoryName;
        option.value = cate.id
        const children: Category[] = this.generateCates(cate.id, cates)
        if (children.length > 0) {
          option.options = [...children.map<IOption<string, number>>(c => {
            const o = new IOption<string, number>()
            o.label = c.categoryName
            o.value = c.id
            return o
          })]
        }
        temArr.push(option)
      }

    })
    return temArr
  }

  async findOne(id: number) {
    const cate = await this.cateRepository
      .findOne({
        select: {
          categoryName: true,
          id: true,
          parentId: true,
          createTime: true,
          icon: true,
          sort: true,
          isActive: true
        }, where: { id }
      })
    return cate;
  }


  async remove(id: number) {
    await this.cateRepository.softDelete(id)
  }
  async removeByIds(ids: string) {
    await this.cateRepository.softDelete(ids)
  }
}
